// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.settings.feeds

import android.content.DialogInterface
import android.os.Bundle
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.textview.MaterialTextView
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.repo.FeedInfo
import xyz.apiote.bimba.czwek.settings.DownloadCitiesWorker
import java.time.format.DateTimeFormatter


class BimbaFeedInfoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
	val root: View = itemView.findViewById(R.id.feed)
	val switch: MaterialSwitch = itemView.findViewById(R.id.feed_switch)
	val name: TextView = itemView.findViewById(R.id.feed_name)
	val switchContainer: ConstraintLayout = itemView.findViewById(R.id.feed_switch_container)

	companion object {
		fun bind(
			feed: FeedInfo,
			feedSettings: FeedSettings?,
			holder: BimbaFeedInfoViewHolder?,
			onClickListener: (String) -> Unit,
			onCheckedChangeListener: (String, Boolean) -> Unit
		) {
			holder?.name?.alpha = if (feed.cached) {
				.5f
			} else {
				1f
			}

			holder?.root?.setOnClickListener {
				onClickListener(feed.id)
			}
			holder?.name?.text = feed.name
			if (feedSettings != null) {
				holder?.switchContainer?.visibility = View.VISIBLE
				holder?.switch?.apply {
					isChecked = feedSettings.enabled
					setOnCheckedChangeListener { _, isChecked ->
						onCheckedChangeListener(feed.id, isChecked)
					}
				}
			} else {
				holder?.switchContainer?.visibility = View.GONE
			}
		}
	}
}

class BimbaFeedInfoAdapter(
	private val inflater: LayoutInflater,
	private var feeds: List<FeedInfo>,
	private var feedsSettings: FeedsSettings,
	private val onClickListener: ((String) -> Unit),
	private val onEnabledChangedListener: ((String, Boolean) -> Unit)
) :
	RecyclerView.Adapter<BimbaFeedInfoViewHolder>() {

	class DiffUtilCallback(
		private val oldFeeds: List<FeedInfo>,
		private val oldSettings: FeedsSettings,
		private val newFeeds: List<FeedInfo>,
		private val newSettings: FeedsSettings
	) : DiffUtil.Callback() {
		override fun getOldListSize() = oldFeeds.size

		override fun getNewListSize() = newFeeds.size

		override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
			oldFeeds[oldItemPosition].id == newFeeds[newItemPosition].id

		override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
			val oldItem = oldFeeds[oldItemPosition]
			val oldSetting = oldSettings[oldItem.id]
			val newItem = newFeeds[newItemPosition]
			val newSetting = newSettings[newItem.id]
			return oldItem.cached == newItem.cached && oldItem.name == newItem.name && oldSetting?.enabled == newSetting?.enabled
		}
	}

	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BimbaFeedInfoViewHolder {
		val rowView = inflater.inflate(R.layout.feedinfo, parent, false)
		return BimbaFeedInfoViewHolder(rowView)
	}

	override fun onBindViewHolder(holder: BimbaFeedInfoViewHolder, position: Int) {
		val feed = feeds[position]
		BimbaFeedInfoViewHolder.bind(
			feed,
			feedsSettings[feed.id],
			holder,
			onClickListener,
			onEnabledChangedListener
		)
	}

	override fun getItemCount(): Int = feeds.size

	fun getItemPosition(feedID: String): Int {
		return feeds.indexOfFirst { it.id == feedID }
	}

	fun update(items: List<FeedInfo>?, settings: FeedsSettings?, notify: Boolean) {
		val diffCallback = DiffUtilCallback(
			feeds,
			feedsSettings,
			items?.sortedBy { "${it.sortGroup()}_${it.name}" } ?: feeds,
			settings ?: feedsSettings
		)
		val diff = DiffUtil.calculateDiff(diffCallback)
		if (items != null) {
			feeds = items.sortedBy { "${it.sortGroup()}_${it.name}" }
		}
		if (settings != null) {
			feedsSettings = settings
		}
		if (notify) {
			diff.dispatchUpdatesTo(this)
		}
	}

	fun isNullOrEmpty(): Boolean = feeds.isEmpty()
}

class FeedBottomSheet(
	private val feedID: String,
	private val feeds: Map<String, FeedInfo>,
	private val feedsSettings: FeedsSettings,
	private val onDismiss: (FeedSettings?) -> Unit
) :
	BottomSheetDialogFragment() {
	companion object {
		const val TAG = "DepartureBottomSheet"
	}

	private var settings: FeedSettings? = null

	override fun onCreateView(
		inflater: LayoutInflater,
		container: ViewGroup?,
		savedInstanceState: Bundle?
	): View {
		val content = inflater.inflate(R.layout.feed_bottom_sheet, container, false)
		val feed = feeds[feedID]!!
		var settings = feedsSettings[feedID]
		content.findViewById<MaterialTextView>(R.id.feed_name).text = feed.name
		content.findViewById<MaterialTextView>(R.id.description).text = feed.description
		content.findViewById<MaterialTextView>(R.id.outdated_info_warning).visibility =
			if (feed.cached) {
				View.VISIBLE
			} else {
				View.GONE
			}
		if (feed.validSince != null && feed.validTill != null) {
			content.findViewById<MaterialTextView>(R.id.timetable_validity).apply {
				visibility = View.VISIBLE
				text = getString(
					R.string.current_timetable_validity,
					feed.validSince.format(DateTimeFormatter.ISO_LOCAL_DATE),
					feed.validTill.format(DateTimeFormatter.ISO_LOCAL_DATE)
				)
			}
		}
		content.findViewById<MaterialTextView>(R.id.attribution).text = feed.attribution
		content.findViewById<MaterialTextView>(R.id.update_time).text =
			getString(R.string.last_update, feed.formatDate())
		content.findViewById<MaterialSwitch>(R.id.onlineSwitch).apply {
			isChecked = settings?.useOnline == true
			setOnCheckedChangeListener { _, isChecked ->
				settings = settings?.copy(useOnline = isChecked) ?: FeedSettings(
					enabled = true,
					useOnline = isChecked,
					useOffline = false,
					autoUpdate = false
				)
				this@FeedBottomSheet.settings = settings
			}
		}

		if (feed.id == "geocoding") {
			content.findViewById<MaterialTextView>(R.id.offlineSwitchLabel).visibility = View.VISIBLE
			content.findViewById<MaterialSwitch>(R.id.offlineSwitch).visibility = View.VISIBLE
			content.findViewById<MaterialTextView>(R.id.autoUpdateSwitchLabel).visibility = View.VISIBLE
			content.findViewById<MaterialSwitch>(R.id.autoUpdateSwitch).apply {
				visibility = View.VISIBLE
				isEnabled = false
			}
			content.findViewById<MaterialTextView>(R.id.updateButtonLabel).visibility = View.VISIBLE
			content.findViewById<MaterialButton>(R.id.updateButton).visibility = View.VISIBLE
			content.findViewById<MaterialTextView>(R.id.lastDownload).visibility = View.VISIBLE
			content.findViewById<View>(R.id.onlineOfflineDivider).visibility = View.VISIBLE
		} else {
			content.findViewById<MaterialTextView>(R.id.offlineSwitchLabel).visibility = View.GONE
			content.findViewById<MaterialSwitch>(R.id.offlineSwitch).visibility = View.GONE
			content.findViewById<MaterialTextView>(R.id.autoUpdateSwitchLabel).visibility = View.GONE
			content.findViewById<MaterialSwitch>(R.id.autoUpdateSwitch).visibility = View.GONE
			content.findViewById<MaterialTextView>(R.id.updateButtonLabel).visibility = View.GONE
			content.findViewById<MaterialButton>(R.id.updateButton).visibility = View.GONE
			content.findViewById<MaterialTextView>(R.id.lastDownload).visibility = View.GONE
			content.findViewById<View>(R.id.onlineOfflineDivider).visibility = View.GONE
		}

		content.findViewById<MaterialSwitch>(R.id.offlineSwitch).apply {
			isChecked = settings?.useOffline == true
			setOnCheckedChangeListener { _, isChecked ->
				content.findViewById<MaterialSwitch>(R.id.autoUpdateSwitch).isEnabled = isChecked
				content.findViewById<MaterialButton>(R.id.updateButton).isEnabled = isChecked
				settings = settings?.copy(useOffline = isChecked) ?: FeedSettings(
					enabled = true,
					useOnline = false,
					useOffline = isChecked,
					autoUpdate = false
				)
				this@FeedBottomSheet.settings = settings
			}
		}

		content.findViewById<MaterialSwitch>(R.id.autoUpdateSwitch).apply {
			isChecked = settings?.autoUpdate == true
			isEnabled = settings?.useOffline == true
			setOnCheckedChangeListener { _, isChecked ->
				settings = settings?.copy(autoUpdate = isChecked) ?: FeedSettings(
					enabled = true,
					useOnline = false,
					useOffline = false,
					autoUpdate = isChecked
				)
				this@FeedBottomSheet.settings = settings
			}
		}

		content.findViewById<MaterialButton>(R.id.updateButton).apply {
			isEnabled = settings?.useOffline == true && !DownloadCitiesWorker.isWorkScheduled(context, "download_cities")
			setOnClickListener {
				val request = OneTimeWorkRequestBuilder<DownloadCitiesWorker>()
					.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
					.build()
				WorkManager.getInstance(requireContext())
					.enqueueUniqueWork("download_cities", ExistingWorkPolicy.KEEP, request)
			}
		}

		context?.let { context ->
			val citiesLastUpdate = PreferenceManager.getDefaultSharedPreferences(context)
				.getLong(DownloadCitiesWorker.LAST_UPDATE_KEY, -1)
			val lastUpdateString = if (citiesLastUpdate > 0) {
				val lastUpdateTime = DateUtils.getRelativeDateTimeString(
					context,
					citiesLastUpdate * DateUtils.SECOND_IN_MILLIS,
					DateUtils.DAY_IN_MILLIS,
					DateUtils.WEEK_IN_MILLIS,
					0
				)
				context.getString(R.string.last_update, lastUpdateTime)
			} else {
				context.getString(R.string.never)
			}
			content.findViewById<MaterialTextView>(R.id.lastDownload).text = lastUpdateString
		}

		return content
	}

	override fun onDismiss(dialog: DialogInterface) {
		onDismiss(settings)
		settings = null
		super.onDismiss(dialog)
	}
}